#include "PrecompiledHeader.h"
#include "ConsoleHud.h"

/*--------------------------------------------------------------------------*/

ConsoleHud::ConsoleHud() :
	IKeyboardDriver(0, true)
{
	AssertModule(g_Shell);
	g_Shell->RegisterLuaFunction("print", sh::print_string);
	g_Shell->RegisterLuaFunction("print_string", sh::print_string);
	g_Shell->RegisterLuaFunction("print_float", sh::print_float);
	g_Shell->RegisterLuaFunction("print_int", sh::print_int);
	g_Shell->RegisterLuaFunction("clear", sh::clear);

	this->m_coutBackup = std::cout.rdbuf();
	this->m_cerrBackup = std::cerr.rdbuf();

	std::cout.rdbuf(this->m_sstream.rdbuf());
	std::cerr.rdbuf(this->m_sstream.rdbuf());

	this->m_deltaLines = 0;
	this->m_inputContent = "";

	/*--------------------------------------------------------------------------*/

	AssertModule(g_Shell);
	this->SetAsShellOutput();

	/*--------------------------------------------------------------------------*/
	AssertModule(g_Resources);
	g_Resources->Get("default", &this->res_contentFont);
	g_Resources->Get("default_mini", &this->res_codesuggests_font);

	this->m_backgroundMaterial = g_Material->GetMaterial("mat_primitives");

	/*--------------------------------------------------------------------------*/

	this->var_console_enabled = g_Shell->AddVar("console_enabled", 1);
}

/*--------------------------------------------------------------------------*/

ConsoleHud::~ConsoleHud()
{
	std::cout.rdbuf(this->m_coutBackup);
	std::cerr.rdbuf(this->m_cerrBackup);
	this->Clear();
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::OnHudRender()
{
	if (var_console_enabled->GetBool())
	{
		this->DrawConsole();
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::DrawConsole()
{
	/*
	 * rendering
	 */
	int linesCount = this->m_lines.size(); //ilosc linii
	int firstLine = 0;
	int LoS = 18;

	if (linesCount < LoS)
	{
		LoS = linesCount;
	}

	/*
	 * Ustawianie ktora linia jest pierwsza [od gory]
	 */
	if (LoS > linesCount)
	{
		firstLine = 0;
	}
	else
	{
		firstLine = linesCount - LoS + this->m_deltaLines;
	}

	/*
	 * Ucinanie glupich bledow z przekroczeniem zakresu
	 */
	if (firstLine < 0)
	{
		firstLine = 0;
		this->m_deltaLines = -(linesCount - LoS);
	}

	if (firstLine > linesCount - LoS)
	{
		firstLine = linesCount - LoS;
		this->m_deltaLines = 0;
	}

	/*
	 * Tło
	 */
	AssertLogC(this->m_backgroundMaterial == NULL, "Console background material is NULL");
	g_Material->BindMaterial(this->m_backgroundMaterial, EMaterialTechnique::Default);

	glm::ivec2 res = g_Settings->GetResolutioni();

	if (!this->m_suggestions.empty())
	{
		g_Renderer->DrawRectangle2D(glm::vec2(0, res.y - 430), glm::vec2(res.x, res.y), NULL, Color(0.0f, 0.0f, 0.0f, 0.8f));
		g_Renderer->DrawLine2D(glm::vec2(0, res.y - 430), glm::vec2(res.x, res.y - 430), NULL, Color::White);
	}
	else
	{
		g_Renderer->DrawRectangle2D(glm::vec2(0, res.y - 400), glm::vec2(res.x, res.y), NULL, Color(0.0f, 0.0f, 0.0f, 0.8f));
		g_Renderer->DrawLine2D(glm::vec2(0, res.y - 400), glm::vec2(res.x, res.y - 400), NULL, Color::White);
	}

	/*
	 * Malowanie kolejnych linii
	 */
	int currentPos = 0;

	float offsetChange = g_Sdl->GetDeltaTime() * 10.0f;

	for (std::list<Label>::iterator p = this->m_lines.begin(); p != this->m_lines.end(); ++p)
	{
		if (currentPos >= firstLine)
		{
			g_Renderer->DrawText2D(p->GetFont(),
			                       p->GetString(),
			                       glm::vec2(10.0f, (float)(res.y - ((currentPos - firstLine + 1) * 20))),
			                       p->GetColor());

			if (currentPos >= firstLine + LoS - 1)
			{
				break;
			}
		}

		++currentPos;
	}

	g_Renderer->DrawText2D(this->res_contentFont, "> " + this->m_inputContent, glm::vec2(10, res.y - 395), Color::Green);
	g_Renderer->DrawText2D(this->res_codesuggests_font, this->m_suggestions, glm::vec2(10, res.y - 420), Color::White);
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::Clear()
{
	this->m_lines.clear();
	this->m_inputHistory.clear();
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::OnHudUpdate()
{
	this->CheckTilda();
	this->ParseIncomingData();
	this->TrimLines();

	if (this->var_console_enabled->GetBool())
	{
		this->HandleKeys();
		g_Keyboard->AddKeyboardDriver(this);
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::HandleKeys()
{
	if (this->key_codeSuggest)
	{
		std::string prefix = "";
		std::vector<const std::string> codesuggests;

		if (this->m_inputContent.length())
		{
			if (this->m_inputContent[0] == '!' || this->m_inputContent[0] == '@')
			{
				prefix = "aa";
				prefix[0] = this->m_inputContent[0];
				prefix[1] = ' ';

				while (this->m_inputContent[0] == '!'
				       || this->m_inputContent[0] == '@'
				       || this->m_inputContent[0] == ' ')
				{
					this->m_inputContent = this->m_inputContent.substr(1, this->m_inputContent.length() - 1);
				}

				g_Shell->GetVars(this->m_inputContent, codesuggests);
			}
			else
			{
				g_Shell->GetCmds(this->m_inputContent, codesuggests);
			}
		}
		else
		{
			g_Shell->GetCmds(this->m_inputContent, codesuggests);
		}

		/*--------------------------------------------------------------------------*/

		this->m_suggestions = "";
		uint size = codesuggests.size();

		if (size == 1)
		{
			this->m_inputContent = prefix + codesuggests[0] + " ";
		}
		else if (size > 1)
		{
			uint sameLetters = 0;
			char currentLetter;
			std::string str;

			while (1)
			{
				currentLetter = codesuggests[0][sameLetters];
				bool allOk = true;

				for (uint i = 1; i < size; ++i)
				{
					if (codesuggests[i][sameLetters] != currentLetter)
					{
						allOk = false;
						break;
					}
				}

				if (allOk)
				{
					++sameLetters;
					str += currentLetter;
				}
				else
				{
					break;
				}
			}

			this->m_inputContent = prefix + str;
			std::string filler = codesuggests[0];

			for (uint i = 0; i < size; ++i)
			{
				this->m_suggestions += codesuggests[i] + "; ";
			}
		}
	}

	/*--------------------------------------------------------------------------*/

	if (this->key_pageUp)
	{
		this->m_deltaLines--;
	}

	/*--------------------------------------------------------------------------*/

	if (this->key_pageDown)
	{
		this->m_deltaLines++;
	}

	/*--------------------------------------------------------------------------*/

	if (this->key_historyUp)
	{
		if (this->m_inputHistory.size() && this->m_inputHistorySelection != this->m_inputHistory.begin())
		{
			this->m_inputHistorySelection--;
			this->m_inputContent = (*this->m_inputHistorySelection);
		}
	}

	/*--------------------------------------------------------------------------*/

	if (this->key_historyDown)
	{
		if (this->m_inputHistory.size() && this->m_inputHistorySelection != --this->m_inputHistory.end())
		{
			this->m_inputHistorySelection++;
			this->m_inputContent = (*this->m_inputHistorySelection);
		}
		else
		{
			this->m_inputContent = "";
		}
	}

	/*--------------------------------------------------------------------------*/

	if (this->key_enter)
	{
		if (this->m_inputContent.length())
		{
			Label cl(this->res_contentFont, "> " + this->m_inputContent, Color::Green);

			this->m_lines.push_back(cl);

			this->m_inputHistory.push_back(this->m_inputContent);
			this->m_inputHistorySelection = this->m_inputHistory.end();

			g_Shell->Execute(this->m_inputContent);
		}

		this->m_inputContent = ""; //kasujemy input
		this->m_deltaLines = 0; //cofamy się do linii frontu
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::ParseIncomingData()
{
	std::string output = this->m_sstream.str();
	std::string newEntry;
	uint length = output.length();

	for (uint i = 0; i < length; ++i)
	{
		if (output[i] == '\n')
		{
			Color colColor = Color::White;

			if (newEntry.length() >= 2 && newEntry[0] == '#')
			{
				colColor.SetFromChar(newEntry[1]);
				newEntry = newEntry.substr(2, newEntry.length());
			}

			Label cl(this->res_contentFont, newEntry, colColor);

			this->m_lines.push_back(cl);
			newEntry = "";
		}
		else
		{
			newEntry += output[i];
		}
	}

	this->m_sstream.str("");
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::TrimLines()
{
	int size = this->m_lines.size();

	if (size > 200)
	{
		while (size > 200)
		{
			this->m_lines.pop_front();
			--size;
		}
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::CheckTilda()
{
	if (g_Keyboard->GetKeyState(EKey::k_console))
	{
		if (this->var_console_enabled->GetBool())
		{
			this->var_console_enabled->Set(0);
		}
		else
		{
			this->var_console_enabled->Set(1);
		}

		g_Keyboard->SetKeyState(EKey::k_console, false);
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::OnKeyboardAction()
{
	g_Keyboard->GetInputText(this->m_inputContent);

	this->key_codeSuggest = g_Keyboard->GetKeyState(EKey::k_tab);

	if (this->key_codeSuggest)
	{
		g_Keyboard->SetKeyState(EKey::k_tab, false);
	}

	this->key_enter = g_Keyboard->GetKeyState(EKey::k_enter);

	if (this->key_enter)
	{
		g_Keyboard->SetKeyState(EKey::k_enter, false);
	}

	this->key_historyDown = g_Keyboard->GetKeyState(EKey::k_down);

	if (this->key_historyDown)
	{
		g_Keyboard->SetKeyState(EKey::k_down, false);
	}

	this->key_historyUp = g_Keyboard->GetKeyState(EKey::k_up);

	if (this->key_historyUp)
	{
		g_Keyboard->SetKeyState(EKey::k_up, false);
	}

	this->key_pageDown = g_Keyboard->GetKeyState(EKey::k_pagedown);

	if (this->key_pageDown)
	{
		g_Keyboard->SetKeyState(EKey::k_pagedown, false);
	}

	this->key_pageUp = g_Keyboard->GetKeyState(EKey::k_pageup);

	if (this->key_pageUp)
	{
		g_Keyboard->SetKeyState(EKey::k_pageup, false);
	}
}

/*--------------------------------------------------------------------------*/

void ConsoleHud::Print(const std::string& message)
{
	this->m_sstream << "#c" << message << "\n";
}

/*--------------------------------------------------------------------------*/

void sh::print_string(const std::string& message)
{
	AssertModule(g_Hud);

	g_Hud->GetConsoleManager()->Print(message);
}

/*--------------------------------------------------------------------------*/

void sh::print_int(int message)
{
	AssertModule(g_Hud);

	std::stringstream ss;
	ss << message;

	g_Hud->GetConsoleManager()->Print(ss.str());
}

/*--------------------------------------------------------------------------*/

void sh::clear()
{
	AssertModule(g_Hud);

	g_Hud->GetConsoleManager()->Clear();
}

/*--------------------------------------------------------------------------*/

void sh::print_float(float message)
{
	AssertModule(g_Hud);

	std::stringstream ss;
	ss << message;

	g_Hud->GetConsoleManager()->Print(ss.str());
}

/*--------------------------------------------------------------------------*/
